home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / packer / gnu-tar / diffarch.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  15KB  |  681 lines

  1. /* Diff files from a tar archive.
  2.    Copyright (C) 1988 Free Software Foundation
  3.  
  4. This file is part of GNU Tar.
  5.  
  6. GNU Tar is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. GNU Tar is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU Tar; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /*
  21.  * Diff files from a tar archive.
  22.  *
  23.  * Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu.
  24.  *
  25.  * @(#) diffarch.c 1.10 87/11/11 - gnu
  26.  */
  27.  
  28. #include <stdio.h>
  29. #include <errno.h>
  30. #include <sys/types.h>
  31. #include <sys/stat.h>
  32.  
  33. #ifdef BSD42
  34. #include <sys/file.h>
  35. #endif
  36.  
  37. #if !defined(MSDOS) && !defined(AMIGA)
  38. #include <sys/ioctl.h>
  39. #if !defined(USG) || defined(HAVE_MTIO)
  40. #include <sys/mtio.h>
  41. #endif
  42. #endif
  43.  
  44. #ifdef USG
  45. #include <fcntl.h>
  46. #endif
  47.  
  48. /* Some systems don't have these #define's -- we fake it here. */
  49. #ifndef O_RDONLY
  50. #define O_RDONLY    0
  51. #endif
  52. #ifndef O_NDELAY
  53. #define O_NDELAY    0
  54. #endif
  55.  
  56. #ifndef S_IFLNK
  57. #define lstat stat
  58. #endif
  59.  
  60. extern int errno;            /* From libc.a */
  61. extern char *valloc();                  /* From libc.a */
  62.  
  63. #include "tar.h"
  64. #include "port.h"
  65. #include "rmt.h"
  66.  
  67. extern union record *head;        /* Points to current tape header */
  68. extern struct stat hstat;        /* Stat struct corresponding */
  69. extern int head_standard;        /* Tape header is in ANSI format */
  70.  
  71. extern void print_header();
  72. extern void skip_file();
  73. extern void skip_extended_headers();
  74.  
  75. extern FILE *msg_file;
  76.  
  77. int now_verifying = 0;        /* Are we verifying at the moment? */
  78.  
  79. int    diff_fd;        /* Descriptor of file we're diffing */
  80.  
  81. char    *diff_buf = 0;        /* Pointer to area for reading
  82.                        file contents into */
  83.  
  84. char    *diff_dir;        /* Directory contents for LF_DUMPDIR */
  85.  
  86. int different = 0;
  87.  
  88. /*struct sp_array *sparsearray;
  89. int        sp_ar_size = 10;*/
  90. /*
  91.  * Initialize for a diff operation
  92.  */
  93. diff_init()
  94. {
  95.  
  96.     /*NOSTRICT*/
  97.     diff_buf = (char *) valloc((unsigned)blocksize);
  98.     if (!diff_buf) {
  99.         msg("could not allocate memory for diff buffer of %d bytes\n",
  100.             blocksize);
  101.         exit(EX_ARGSBAD);
  102.     }
  103. }
  104.  
  105. /*
  106.  * Diff a file against the archive.
  107.  */
  108. void
  109. diff_archive()
  110. {
  111.     register char *data;
  112.     int check, namelen;
  113.     int err;
  114.     long offset;
  115.     struct stat filestat;
  116.     char linkbuf[NAMSIZ+3];
  117.     int compare_chunk();
  118.     int compare_dir();
  119.     dev_t    dev;
  120.     ino_t    ino;
  121.     char *get_dir_contents();
  122.     long from_oct();
  123.     /* long lseek(); REMOVED AMIGA */
  124.  
  125.     errno = EPIPE;            /* FIXME, remove perrors */
  126.  
  127.     saverec(&head);                 /* Make sure it sticks around */
  128.     userec(head);                   /* And go past it in the archive */
  129.     decode_header(head, &hstat, &head_standard, 1); /* Snarf fields */
  130.  
  131.     /* Print the record from 'head' and 'hstat' */
  132.     if (f_verbose) {
  133.         if(now_verifying)
  134.             fprintf(msg_file,"Verify ");
  135.         print_header();
  136.     }
  137.  
  138.     switch (head->header.linkflag) {
  139.  
  140.     default:
  141.         msg("Unknown file type '%c' for %s, diffed as normal file\n",
  142.             head->header.linkflag, head->header.name);
  143.         /* FALL THRU */
  144.  
  145.     case LF_OLDNORMAL:
  146.     case LF_NORMAL:
  147.     case LF_SPARSE:
  148.     case LF_CONTIG:
  149.         /*
  150.          * Appears to be a file.
  151.          * See if it's really a directory.
  152.          */
  153.         namelen = strlen(head->header.name)-1;
  154.         if (head->header.name[namelen] == '/')
  155.             goto really_dir;
  156.  
  157.  
  158.         if(do_stat(&filestat)) {
  159.             if (head->header.isextended)
  160.                 skip_extended_headers();
  161.             skip_file((long)hstat.st_size);
  162.             different++;
  163.             goto quit;
  164.         }
  165.  
  166.         if ((filestat.st_mode & S_IFMT) != S_IFREG) {
  167.             fprintf(msg_file, "%s: not a regular file\n",
  168.                 head->header.name);
  169.             skip_file((long)hstat.st_size);
  170.             different++;
  171.             goto quit;
  172.         }
  173.  
  174.         filestat.st_mode &= ~S_IFMT;
  175.         if (filestat.st_mode != hstat.st_mode)
  176.             sigh("mode");
  177.         if (filestat.st_uid  != hstat.st_uid)
  178.             sigh("uid");
  179.         if (filestat.st_gid  != hstat.st_gid)
  180.             sigh("gid");
  181.         if (filestat.st_mtime != hstat.st_mtime)
  182.             sigh("mod time");
  183.         if (head->header.linkflag != LF_SPARSE &&
  184.                 filestat.st_size != hstat.st_size) {
  185.             sigh("size");
  186.             skip_file((long)hstat.st_size);
  187.             goto quit;
  188.         }
  189.  
  190.         diff_fd = open(head->header.name, O_NDELAY|O_RDONLY);
  191.  
  192.         if (diff_fd < 0 && !f_absolute_paths) {
  193.             char tmpbuf[NAMSIZ+2];
  194.  
  195.             tmpbuf[0]='/';
  196.             strcpy(&tmpbuf[1],head->header.name);
  197.             diff_fd=open(tmpbuf, O_NDELAY|O_RDONLY);
  198.         }
  199.         if (diff_fd < 0) {
  200.             msg_perror("cannot open %s",head->header.name);
  201.             if (head->header.isextended)
  202.                 skip_extended_headers();
  203.             skip_file((long)hstat.st_size);
  204.             different++;
  205.             goto quit;
  206.         }
  207.         /*
  208.          * Need to treat sparse files completely differently here.
  209.          */
  210.         if (head->header.linkflag == LF_SPARSE)
  211.             diff_sparse_files(hstat.st_size);
  212.         else
  213.             wantbytes((long)(hstat.st_size),compare_chunk);
  214.  
  215.         check = close(diff_fd);
  216.         if (check < 0)
  217.             msg_perror("Error while closing %s",head->header.name);
  218.  
  219.     quit:
  220.         break;
  221.  
  222.     case LF_LINK:
  223.         if(do_stat(&filestat))
  224.             break;
  225.         dev = filestat.st_dev;
  226.         ino = filestat.st_ino;
  227.         err = stat(head->header.linkname, &filestat);
  228.         if (err < 0) {
  229.             if (errno==ENOENT) {
  230.                 fprintf(msg_file, "%s: does not exist\n",head->header.name);
  231.             } else {
  232.                 msg_perror("cannot stat file %s",head->header.name);
  233.             }
  234.             different++;
  235.             break;
  236.         }
  237.         if(filestat.st_dev!=dev || filestat.st_ino!=ino) {
  238.             fprintf(msg_file, "%s not linked to %s\n",head->header.name,head->header.linkname);
  239.             break;
  240.         }
  241.         break;
  242.  
  243. #ifdef S_IFLNK
  244.     case LF_SYMLINK:
  245.         check = readlink(head->header.name, linkbuf,
  246.                  (sizeof linkbuf)-1);
  247.  
  248.         if (check < 0) {
  249.             if (errno == ENOENT) {
  250.                 fprintf(msg_file,
  251.                     "%s: no such file or directory\n",
  252.                     head->header.name);
  253.             } else {
  254.                 msg_perror("cannot read link %s",head->header.name);
  255.             }
  256.             different++;
  257.             break;
  258.         }
  259.  
  260.         linkbuf[check] = '\0';  /* Null-terminate it */
  261.         if (strncmp(head->header.linkname, linkbuf, check) != 0) {
  262.             fprintf(msg_file, "%s: symlink differs\n",
  263.                 head->header.linkname);
  264.             different++;
  265.         }
  266.         break;
  267. #endif
  268.  
  269.     case LF_CHR:
  270.         hstat.st_mode |= S_IFCHR;
  271.         goto check_node;
  272.  
  273. #ifdef S_IFBLK
  274.     /* If local system doesn't support block devices, use default case */
  275.     case LF_BLK:
  276.         hstat.st_mode |= S_IFBLK;
  277.         goto check_node;
  278. #endif
  279.  
  280. #ifdef S_IFIFO
  281.     /* If local system doesn't support FIFOs, use default case */
  282.     case LF_FIFO:
  283.         hstat.st_mode |= S_IFIFO;
  284.         hstat.st_rdev = 0;        /* FIXME, do we need this? */
  285.         goto check_node;
  286. #endif
  287.  
  288.     check_node:
  289.         /* FIXME, deal with umask */
  290.         if(do_stat(&filestat))
  291.             break;
  292.         if(hstat.st_rdev != filestat.st_rdev) {
  293.             fprintf(msg_file, "%s: device numbers changed\n", head->header.name);
  294.             different++;
  295.             break;
  296.         }
  297.         if(hstat.st_mode != filestat.st_mode) {
  298.             fprintf(msg_file, "%s: mode or device-type changed\n", head->header.name);
  299.             different++;
  300.             break;
  301.         }
  302.         break;
  303.  
  304.     case LF_DUMPDIR:
  305.         data=diff_dir=get_dir_contents(head->header.name,0);
  306.         wantbytes((long)(hstat.st_size),compare_dir);
  307.         free(data);
  308.         /* FALL THROUGH */
  309.  
  310.     case LF_DIR:
  311.         /* Check for trailing / */
  312.         namelen = strlen(head->header.name)-1;
  313.     really_dir:
  314.         while (namelen && head->header.name[namelen] == '/')
  315.             head->header.name[namelen--] = '\0';    /* Zap / */
  316.  
  317.         if(do_stat(&filestat))
  318.             break;
  319.         if((filestat.st_mode&S_IFMT)!=S_IFDIR) {
  320.             fprintf(msg_file, "%s is no longer a directory\n",head->header.name);
  321.             different++;
  322.             break;
  323.         }
  324.         if((filestat.st_mode&~S_IFMT) != hstat.st_mode)
  325.             sigh("mode");
  326.         break;
  327.  
  328.     case LF_VOLHDR:
  329.         break;
  330.  
  331.     case LF_MULTIVOL:
  332.         namelen = strlen(head->header.name)-1;
  333.         if (head->header.name[namelen] == '/')
  334.             goto really_dir;
  335.  
  336.         if(do_stat(&filestat))
  337.             break;
  338.  
  339.         if ((filestat.st_mode & S_IFMT) != S_IFREG) {
  340.             fprintf(msg_file, "%s: not a regular file\n",
  341.                 head->header.name);
  342.             skip_file((long)hstat.st_size);
  343.             different++;
  344.             break;
  345.         }
  346.  
  347.         filestat.st_mode &= ~S_IFMT;
  348.         offset = from_oct(1+12, head->header.offset);
  349.         if (filestat.st_size != hstat.st_size + offset) {
  350.             sigh("size");
  351.             skip_file((long)hstat.st_size);
  352.             different++;
  353.             break;
  354.         }
  355.  
  356.         diff_fd = open(head->header.name, O_NDELAY|O_RDONLY);
  357.  
  358.         if (diff_fd < 0) {
  359.             msg_perror("cannot open file %s",head->header.name);
  360.             skip_file((long)hstat.st_size);
  361.             different++;
  362.             break;
  363.         }
  364.         err = lseek(diff_fd, offset, 0);
  365.         if(err!=offset) {
  366.             msg_perror("cannot seek to %ld in file %s",offset,head->header.name);
  367.             different++;
  368.             break;
  369.         }
  370.  
  371.         wantbytes((long)(hstat.st_size),compare_chunk);
  372.  
  373.         check = close(diff_fd);
  374.         if (check < 0) {
  375.             msg_perror("Error while closing %s",head->header.name);
  376.         }
  377.         break;
  378.  
  379.     }
  380.  
  381.     /* We don't need to save it any longer. */
  382.     saverec((union record **) 0);   /* Unsave it */
  383. }
  384.  
  385. int
  386. compare_chunk(bytes,buffer)
  387. long bytes;
  388. char *buffer;
  389. {
  390.     int err;
  391.  
  392.     err=read(diff_fd,diff_buf,bytes);
  393.     if(err!=bytes) {
  394.         if(err<0) {
  395.             msg_perror("can't read %s",head->header.name);
  396.         } else {
  397.             fprintf(msg_file,"%s: could only read %d of %d bytes\n",head->header.name,err,bytes);
  398.         }
  399.         different++;
  400.         return -1;
  401.     }
  402.     if(bcmp(buffer,diff_buf,bytes)) {
  403.         fprintf(msg_file, "%s: data differs\n",head->header.name);
  404.         different++;
  405.         return -1;
  406.     }
  407.     return 0;
  408. }
  409.  
  410. int
  411. compare_dir(bytes,buffer)
  412. long bytes;
  413. char *buffer;
  414. {
  415.     if(bcmp(buffer,diff_dir,bytes)) {
  416.         fprintf(msg_file, "%s: data differs\n",head->header.name);
  417.         different++;
  418.         return -1;
  419.     }
  420.     diff_dir+=bytes;
  421.     return 0;
  422. }
  423.  
  424. /*
  425.  * Sigh about something that differs.
  426.  */
  427. sigh(what)
  428.     char *what;
  429. {
  430.  
  431.     fprintf(msg_file, "%s: %s differs\n",
  432.         head->header.name, what);
  433. }
  434.  
  435. verify_volume()
  436. {
  437.     int status;
  438. #ifdef MTIOCTOP
  439.     struct mtop t;
  440.     int er;
  441. #endif
  442.  
  443.     if(!diff_buf)
  444.         diff_init();
  445. #ifdef MTIOCTOP
  446.     t.mt_op = MTBSF;
  447.     t.mt_count = 1;
  448.     if((er=rmtioctl(archive,MTIOCTOP,&t))<0) {
  449.         if(errno!=EIO || (er=rmtioctl(archive,MTIOCTOP,&t))<0) {
  450. #endif
  451.             if(rmtlseek(archive,0L,0)!=0) {
  452.                 /* Lseek failed.  Try a different method */
  453.                 msg_perror("Couldn't rewind archive file for verify");
  454.                 return;
  455.             }
  456. #ifdef MTIOCTOP
  457.         }
  458.     }
  459. #endif
  460.     ar_reading=1;
  461.     now_verifying = 1;
  462.     fl_read();
  463.     for(;;) {
  464.         status = read_header();
  465.         if(status==0) {
  466.             unsigned n;
  467.  
  468.             n=0;
  469.             do {
  470.                 n++;
  471.                 status=read_header();
  472.             } while(status==0);
  473.             msg("VERIFY FAILURE: %d invalid header%s detected!",n,n==1?"":"s");
  474.         }
  475.         if(status==2 || status==EOF)
  476.             break;
  477.         diff_archive();
  478.     }
  479.     ar_reading=0;
  480.     now_verifying = 0;
  481.  
  482. }
  483.  
  484. int do_stat(statp)
  485. struct stat *statp;
  486. {
  487.     int err;
  488.  
  489.     err = f_follow_links ? stat(head->header.name, statp) : lstat(head->header.name, statp);
  490.     if (err < 0) {
  491.         if (errno==ENOENT) {
  492.             fprintf(msg_file, "%s: does not exist\n",head->header.name);
  493.         } else
  494.             msg_perror("can't stat file %s",head->header.name);
  495. /*        skip_file((long)hstat.st_size);
  496.         different++;*/
  497.         return 1;
  498.     } else
  499.         return 0;
  500. }
  501.  
  502. /*
  503.  * JK
  504.  * Diff'ing a sparse file with its counterpart on the tar file is a
  505.  * bit of a different story than a normal file.  First, we must know
  506.  * what areas of the file to skip through, i.e., we need to contruct
  507.  * a sparsearray, which will hold all the information we need.    We must
  508.  * compare small amounts of data at a time as we find it.
  509.  */
  510.  
  511. diff_sparse_files(filesize)
  512. int    filesize;
  513.  
  514. {
  515.     int        sparse_ind = 0;
  516.     char        *buf;
  517.     int        buf_size = RECORDSIZE;
  518.     union record    *datarec;
  519.     int        err;
  520.     long        numbytes;
  521.     int        amt_read = 0;
  522.     int        size = filesize;
  523.  
  524.     buf = (char *) malloc(buf_size * sizeof (char));
  525.  
  526.     fill_in_sparse_array();
  527.  
  528.  
  529.     while (size > 0) {
  530.         datarec = findrec();
  531.         if (!sparsearray[sparse_ind].numbytes)
  532.             break;
  533.  
  534.         /*
  535.          * 'numbytes' is nicer to write than
  536.          * 'sparsearray[sparse_ind].numbytes' all the time ...
  537.          */
  538.         numbytes = sparsearray[sparse_ind].numbytes;
  539.  
  540.         lseek(diff_fd, sparsearray[sparse_ind].offset, 0);
  541.         /*
  542.          * take care to not run out of room in our buffer
  543.          */
  544.         while (buf_size < numbytes) {
  545.             buf = (char *) realloc(buf, buf_size * 2 * sizeof(char));
  546.             buf_size *= 2;
  547.         }
  548.         while (numbytes > RECORDSIZE) {
  549.             if ((err = read(diff_fd, buf, RECORDSIZE)) != RECORDSIZE) {
  550.                 if (err < 0)
  551.                     msg_perror("can't read %s", head->header.name);
  552.                 else
  553.                     fprintf(msg_file, "%s: could only read %d of %d bytes\n",
  554.                         err, numbytes);
  555.                 break;
  556.             }
  557.             if (bcmp(buf, datarec->charptr, RECORDSIZE)) {
  558.                 different++;
  559.                 break;
  560.             }
  561.             numbytes -= err;
  562.             size -= err;
  563.             userec(datarec);
  564.             datarec = findrec();
  565.         }
  566.         if ((err = read(diff_fd, buf, numbytes)) != numbytes) {
  567.             if (err < 0)
  568.                 msg_perror("can't read %s", head->header.name);
  569.             else
  570.                 fprintf(msg_file, "%s: could only read %d of %d bytes\n",
  571.                         err, numbytes);
  572.             break;
  573.         }
  574.  
  575.         if (bcmp(buf, datarec->charptr, numbytes)) {
  576.             different++;
  577.             break;
  578.         }
  579. /*        amt_read += numbytes;
  580.         if (amt_read >= RECORDSIZE) {
  581.             amt_read = 0;
  582.             userec(datarec);
  583.             datarec = findrec();
  584.         }*/
  585.         userec(datarec);
  586.         sparse_ind++;
  587.         size -= numbytes;
  588.     }
  589.     /*
  590.      * if the number of bytes read isn't the
  591.      * number of bytes supposedly in the file,
  592.      * they're different
  593.      */
  594. /*    if (amt_read != filesize)
  595.         different++;*/
  596.     userec(datarec);
  597.     free(sparsearray);
  598.     if (different)
  599.         fprintf(msg_file, "%s: data differs\n", head->header.name);
  600.  
  601. }
  602.  
  603. /*
  604.  * JK
  605.  * This routine should be used more often than it is ... look into
  606.  * that.  Anyhow, what it does is translate the sparse information
  607.  * on the header, and in any subsequent extended headers, into an
  608.  * array of structures with true numbers, as opposed to character
  609.  * strings.  It simply makes our life much easier, doing so many
  610.  * comparisong and such.
  611.  */
  612. fill_in_sparse_array()
  613. {
  614.     int    ind;
  615.  
  616.     /*
  617.      * allocate space for our scratch space; it's initially
  618.      * 10 elements long, but can change in this routine if
  619.      * necessary
  620.      */
  621.     sp_array_size = 10;
  622.     sparsearray = (struct sp_array *) malloc(sp_array_size * sizeof(struct sp_array));
  623.  
  624.     /*
  625.      * there are at most five of these structures in the header
  626.      * itself; read these in first
  627.      */
  628.     for (ind = 0; ind < SPARSE_IN_HDR; ind++) {
  629.         if (!head->header.sp[ind].numbytes)
  630.             break;
  631.         sparsearray[ind].offset =
  632.             from_oct(1+12, head->header.sp[ind].offset);
  633.         sparsearray[ind].numbytes =
  634.             from_oct(1+12, head->header.sp[ind].numbytes);
  635.     }
  636.     /*
  637.      * if the header's extended, we gotta read in exhdr's till
  638.      * we're done
  639.      */
  640.     if (head->header.isextended) {
  641.         /* how far into the sparsearray we are 'so far' */
  642.         static int so_far_ind = SPARSE_IN_HDR;
  643.         union record *exhdr;
  644.  
  645.         for (;;) {
  646.         exhdr = findrec();
  647.         for (ind = 0; ind < SPARSE_EXT_HDR; ind++) {
  648.             if (ind+so_far_ind > sp_array_size-1) {
  649.                 /*
  650.                  * we just ran out of room in our
  651.                  *  scratch area - realloc it
  652.                  */
  653.                 sparsearray = (struct sp_array *)
  654.                     realloc(sparsearray,
  655.                         sp_array_size*2*sizeof(struct sp_array));
  656.                 sp_array_size *= 2;
  657.             }
  658.             /*
  659.              * convert the character strings into longs
  660.              */
  661.             sparsearray[ind+so_far_ind].offset =
  662.                 from_oct(1+12, exhdr->ext_hdr.sp[ind].offset);
  663.             sparsearray[ind+so_far_ind].numbytes =
  664.                 from_oct(1+12, exhdr->ext_hdr.sp[ind].numbytes);
  665.         }
  666.         /*
  667.          * if this is the last extended header for this
  668.          * file, we can stop
  669.          */
  670.         if (!exhdr->ext_hdr.isextended)
  671.             break;
  672.         else {
  673.             so_far_ind += SPARSE_EXT_HDR;
  674.             userec(exhdr);
  675.         }
  676.         }
  677.         /* be sure to skip past the last one  */
  678.         userec(exhdr);
  679.     }
  680. }
  681.